
use axum::{
    body::Bytes, extract::MatchedPath, http::{HeaderMap, Request}, response::Response, routing::{get,post}, Json, Router
};
use serde::Serialize;

//跨域
// use tower_http::cors::CorsLayer;

// 日志相关
use std::time::Duration;
use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer};
use tracing::{info_span, Span};

//handler
use fortune_telling_server::handler;

#[tokio::main]
async fn main() {
    dotenv::dotenv().ok(); // 可以使用 std::env::var(变量名) 访问 .env中的变量

    //路由
    let router = Router::new()

    //http日志监听
    .layer(
        TraceLayer::new_for_http()
            .make_span_with(|request: &Request<_>| {
                // Log the matched route's path (with placeholders not filled in).
                // Use request.uri() or OriginalUri if you want the real path.
                let matched_path = request
                    .extensions()
                    .get::<MatchedPath>()
                    .map(MatchedPath::as_str);

                info_span!(
                    "http_request",
                    method = ?request.method(),
                    matched_path,
                    some_other_field = tracing::field::Empty,
                )
            })
            .on_request(|_request: &Request<_>, _span: &Span| {
                // You can use `_span.record("some_other_field", value)` in one of these
                // closures to attach a value to the initially empty field in the info_span
                // created above.
            })
            .on_response(|_response: &Response, _latency: Duration, _span: &Span| {
                // ...
            })
            .on_body_chunk(|_chunk: &Bytes, _latency: Duration, _span: &Span| {
                // ...
            })
            .on_eos(
                |_trailers: Option<&HeaderMap>, _stream_duration: Duration, _span: &Span| {
                    // ...
                },
            )
            .on_failure(
                |_error: ServerErrorsFailureClass, _latency: Duration, _span: &Span| {
                    // ...
                },
            ),
    )

    .route("/conn", get(conn)) //测试连接
    .route("/api/login", post(handler::auth::login)) //密码登录
    .route("/api/wxmn_login", post(handler::auth::wxmn_login)) //微信小程序登录
    .route("/api/regist", post(handler::auth::regist)) //注册
    .route("/api/user/user_info", get(handler::user::user_info))//登录人获自己的用户信息
    .route("/api/test", get(api_test)) //api测试
    .route("/api/divination/create_by_gpt", post(handler::divination::create_by_gpt))//抽签占卜
    .fallback(fallback) //路由失败
    ;

    //服务域名
    let base_url = std::env::var("BASE_URL").expect("BASE_URL not set");
    //server
    println!("服务已创建,监听: {}", &base_url);
    tracing::info!("服务已创建,监听: {}", &base_url);
    let listener = tokio::net::TcpListener::bind(&base_url).await.unwrap();
    axum::serve(listener, router.into_make_service())
        .await
        .unwrap();
}

//路由失败 404
pub async fn fallback(uri: axum::http::Uri) -> impl axum::response::IntoResponse {
    (
        axum::http::StatusCode::NOT_FOUND,
        format!("No route {}", uri),
    )
}

//服务连通测试
pub async fn conn() -> (axum::http::StatusCode, String) {
    (axum::http::StatusCode::OK, "Server Connect OK".to_string())
}

//api接口测试
#[derive(Serialize)]
struct ResData {
    code: u32,
    msg: String,
}
async fn api_test() -> Json<ResData> {
    Json(ResData {
        code: 200,
        msg: "api_test".to_string(),
    })
}